// 模块作用说明：寄存器是CPU内部用于暂存数据的高速存储器。本题要求设计一个具有
// 写入和两个读取端口的寄存器堆，可支持并发读取和同步写入，是RISC处理器的关键部
// 件。
// 1 module regfile (
//  2
//  3
//  4
//  5
//  6
//  7
//  8 );
//  input wire clk,
//  input wire rst,
//  input wire we,
//  input wire [1:0] raddr1, raddr2, waddr,
//  input wire [7:0] wdata,
//  output wire [7:0] rdata1, rdata2
//  Listing 2: 寄存器文件模块接口定义
// 要求：
// 2
// • 包含4个8位寄存器（r0-r3）；
// • 上升沿写入，we=1时更新；
// • 模块支持同步复位：当rst为高电平时，在下一个时钟上升沿将所有寄存器清零。
// • 同时支持两个寄存器并发读取。
// 设计目的：
// 寄存器组是CPU中用于高速数据交换的核心结构。在典型的RISC架构中，指令往往遵
// 循“从两个寄存器中读取操作数，并将结果写回目标寄存器”的三操作数格式。因此，我
// 们设计该模块时：
// • 设置两个独立读取端口（raddr1 和 raddr2），便于在一个时钟周期内同时取出两个源
// 操作数；
// • 设置一个写入端口，支持在时钟上升沿将结果写入目标寄存器；
// • 使用4个寄存器可以满足初步的实验需求，并能简化指令译码与模拟执行；
// • 该设计是连接ALU和控制单元的桥梁，后续整合时会作为数据通路的重要组成部分。
// 通过这个题目，学生不仅可以熟悉Verilog 中的数组建模技巧和时序写入逻辑，还能直观
// 理解CPU是如何进行寄存器级操作的，为最终整合出一个简化CPU打下坚实基础。



module regfile(
    input wire clk,//时钟信号
    input wire rst,//复位信号 高电平有效
    input wire we,//写使能信号
    input wire[1:0] raddr1, raddr2, waddr, 
    input wire[7:0] wdata,
    output wire[7:0] rdata1,rdata2
);

    reg[7:0] regs[3:0]; //定义4个8位寄存器

    //同步复位
    always @(posedge clk) begin
        if(rst) begin //时钟信号为上升沿且rst为高电平时执行复位
            regs[0] <= 8'b0;
            regs[1] <= 8'b0;
            regs[2] <= 8'b0;
            regs[3] <= 8'b0;
        end
        else if(we && !rst) begin//时钟信号为上升沿且是写使能信号且没有收到复位信号
            regs[waddr] <= wdata;
        end
    end

    assign rdata1 = (we&& (raddr1 == waddr)) ? wdata : regs[raddr1];
    assign rdata2 = (we&& (raddr2 == waddr)) ? wdata : regs[raddr2];
endmodule

//测试文件
`timescale 1ns/100ps

module regfile_tb;
    //信号声明
    reg clk;
    reg rst;
    reg we;
    reg[1:0] raddr1,raddr2,waddr;
    reg[7:0] wdata;
    wire[7:0] rdata1,rdata2;

    regfile uut(
        .clk(clk),
        .rst(rst),
        .we(we),
        .raddr1(raddr1),
        .raddr2(raddr2),
        .waddr(waddr),
        .wdata(wdata),
        .rdata1(rdata1),
        .rdata2(rdata2)
    );

    //生成时钟信号
    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end

    //在时钟上升沿 寄存器状态才会更新
    initial begin
        //先初始化信号
        rst = 1;
        we = 0;
        raddr1 = 2'b00;
        raddr2 = 2'b00;
        waddr = 2'b00;
        wdata = 8'd0;

        // 等待第一个时钟上升沿（复位生效）
        @(posedge clk);
        #1;  // 小延迟确保稳定
        
        // 取消复位
        rst = 0;
        @(posedge clk);
        #1;

        //测试写入功能
        $display("---- write ----");
        we = 1;
        waddr = 2'b01;
        wdata = 8'd5;
        @(posedge clk);  // 上升沿触发写入
        #1;
        
        // 读取r1
        raddr1 = 2'b01;
        @(posedge clk);  // 下一个上升沿读取
        #1;
        $display("rdata1: %d", rdata1);

  
        $display("---- read ----");
        waddr = 2'b10;
        wdata = 8'd6;
        raddr1 = 2'b01;
        raddr2 = 2'b10;
        @(posedge clk);  // 上升沿触发写入和读取
        #1;
        $display("predict out: rdata1=5, rdata2=6");
        $display("real out: rdata1=%d, rdata2=%d", rdata1, rdata2);

        // 复位
        $display("---- reset ----");
        rst = 1;
        @(posedge clk);  // 复位生效（regs被清零）
        #1;
        // 多等待几个时钟周期，确保寄存器稳定在复位状态
        repeat(3) @(posedge clk); 
        rst = 0;         
        @(posedge clk);  // 等待下一个边沿确保稳定
        #1;

        // 读取r1和r2
        raddr1 = 2'b01;
        raddr2 = 2'b10;
        @(posedge clk);  // 在新的边沿读取
        #1;
        $display("predict reset: rdata1=0, rdata2=0");
        $display("real reset: rdata1=%d, rdata2=%d", rdata1, rdata2);

        $display("---- finish ----");
        $stop;
    end
endmodule